/**
 *
 *
 *
 *
 */
$.widget("ui.TagsEditor", {
	
	// default options
	options: {		
		// The base URL where the LMF server is deployed, ends with slash
		serverBaseURL: "http://localhost:8080/KiWi2/",

		// The URI of the resource to be tagged; defaults to current document
		resourceURI: document.location.toString().split('#')[0]
		//resourceURI: "http://localhost:8080/KiWi2/resource/test12"
	},

	//////////////////
	// Private Implementation
	//////////////////

	_tagsMap : {},

	_create: function() {
		// Create the skeleton UI
		this._createElements();

		// Create the resource. Needed as we will be tagging it.
		this._createResource();
	},
	
	/**
	 * Logs to browser console if it exists. Otherwise is silent.
	 * 
	 */
	_log : function(s){
		try {
			if (typeof console != "undefined") {
				console.log(s);
			}
		} catch (e) {
			// Just ignore, nothing to do about this
		}
	},


	//////////////////
	// Private Implementation - User Interface
	//////////////////

	_createElements : function(){
		$(this.element).html(
			"\
			<div class='TagsEditor'>\
				<div class='tagsContainer'>\
					<p class='notags'>This URI has no tags yet.</p>\
				</div>\
				<div class='input'></div>\
				<div class='logoCloseButton'></div>\
			</div>\
			"
		);

		this._tagsContainer = $(this.element).find("div.tagsContainer");

		// Bind event handlers: Click within the tag list - in the delete button
		this._tagsContainer.click($.proxy(function(e){
			var target = e.target;
			if ((target.tagName == "SPAN") && (target.className = "delete")) {
				var tagURI = $(target).parent("div.tag").attr("taguri");
				this._removeTagging(tagURI);
			}
		},this));

		// Bind Event handler: Click on the LMF logo - in case of "IFramed" instance
		// collapses the editor
		if (this.options.outterWindowURL) {
			$(this.element).find("div.logoCloseButton").click($.proxy(function(){
				$(document.body).addClass("collapsed");
				window.top.location = this.options.outterWindowURL + "#collapsed";
			}, this));
		}

		// Create an instance of the HierarchyBrowser widget
		// and keep referenceto it
		$(this.element).find("div.input").HierarchyBrowser({
			buttonClick : $.proxy(function(value){
				this._addTagByLabel(value)
			}, this),

			suggestionClicked : $.proxy(function(tagURI, label, schemeURI){
				this._addTagByURI(tagURI, label, schemeURI)
			}, this)	
		});
		this.hierarchyBrowserWidget = $(this.element).find("div.input").data("HierarchyBrowser");

	},

	/**
	 * Renders all tags in the UI
	 *
	 */
	_renderTags : function(){
		$.each(this._tagsMap, $.proxy(function(tagURI, tag) {
			this._renderTag(tag);
		},this));

		this._updateNoTagsMessage();
	},

	/**
	 * Renders the tag in the UI
	 *
	 */
	_renderTag : function(tag){
		var isControlled = false;
		if (tag.schemeURI) {
			isControlled = true;
		}

		var divClassName="tag";
		if (isControlled) {
			divClassName += " controlled";
		}

		this._tagsContainer.append(
			"<div class='" + divClassName + "' taguri='" + tag.URI + "'>"
				+ "<span>" + tag.label + "</span>"
				+ "<span class='delete'></span>"
			+ "</div>"
		);
	},

	/**
	 * Removes the tag from the UI
	 */
	_unrenderTag : function(tagURI){
		this._tagsContainer.find("div[taguri='" + tagURI + "']").remove();
		this._updateNoTagsMessage();
	},

	_updateNoTagsMessage : function(){
		var hasTags = false;
		$.each(this._tagsMap, function(){
			hasTags = true;
			return false;
		});

		if (hasTags) {
			this._tagsContainer.removeClass("notags");
		} else {
			this._tagsContainer.addClass("notags");
		}
	},

	//////////////////
	// Private Implementation - Web Services invocation
	//////////////////

	/**
	 * Creates the resource in the LMF server.
	 * This is called every time the widget starts as the resource needs to
	 * be created before it can be tagged.
	 *
	 * Calling this multiple times shouldn't hurt.
	 */
	_createResource : function(){
		this._log("WS: Creating resource " + this.options.resourceURI);

		// If we can post directly to the resource URI (it's on the server path), do it.
		// Otherwise, the resource URI needs to be of kind:
		// http://localhost:8080/KiWi2/resource/http%3A%2F%2Fgoogle.com%2Fres
//		var postURL = this.options.resourceURI;
//		if (this.options.resourceURI.indexOf(this.options.serverBaseURL + "resource/") != 0) {
//			postURL = this.options.serverBaseURL + "resource/"
//				+ encodeURIComponent(this.options.resourceURI);
//		}
        var postURL = this.options.serverBaseURL + "resource"
					+ "?uri=" + encodeURIComponent(this.options.resourceURI)


		// Invoke the WS

		// TODO: Here is an issue; If the resource exists, the WS will
		// send 302 redirect. However, according to W3 spec (and most of newest
		// implementations follow this), XHR automatically follows the redirects
		// This is not very useful. It would be more useful to be able to catch
		// the redirect.
		jQuery.ajax({
			url: postURL,
			type: "POST",
			success: $.proxy(function(data, extStatus, xhr){
				this._log("WS: Creating resource successful");
				this._getTags();
			},this),
			error: $.proxy(function(xhr, textStatus, errorThrown) {
				this._log("WS: Creating resource failed" + xhr.st);
				this._getTags();
			},this)
		});
	},


	/**
	 * This actually invokes two webservices. It gets the tags and also gets
	 * the taggings. This is needed as in order to detach a tag, we actually
	 * need to tagging URI.
	 */
	_getTags : function(){
		var receivedTags = null;
		var receivedTaggings = null;

		// Get the taggings
		this._log("WS: Get taggings");
		jQuery.ajax({
			url: this.options.serverBaseURL + "social/tagging/list"
					+ "?taggedItemUri=" + encodeURIComponent(this.options.resourceURI),
			type: "GET",
			success: $.proxy(function(data, extStatus, xhr){
				this._log("WS: Received taggings");
				
				// Keep reference on taggings
				receivedTaggings = data;

				// And we got both tags and taggings, process them
				if (receivedTags && receivedTaggings) {
					this._onReceivedTagsAndTaggings(receivedTags, receivedTaggings);
				}
			},this),
			error: $.proxy(function(xhr, textStatus, errorThrown) {
				this._log("WS: Failed to get taggings");
			},this)
		});

		// Get the tags
		this._log("WS: Get tags");
		jQuery.ajax({
			url: this.options.serverBaseURL + "social/tag/list/item"
					+ "?taggedItemUri=" + encodeURIComponent(this.options.resourceURI),
			type: "GET",
			success: $.proxy(function(data, extStatus, xhr){
				this._log("WS: Received tags");
				
				// Keep reference on tags
				receivedTags = data;

				// And we got both tags and taggings, process them
				if (receivedTags && receivedTaggings) {
					this._onReceivedTagsAndTaggings(receivedTags, receivedTaggings);
				}
			},this),
			error: $.proxy(function(xhr, textStatus, errorThrown) {
				this._log("WS: Failed to get tags");
			},this)
		});
	},

	/**
	 * Callend when we receive both tags and taggings.
	 *
	 */
	_onReceivedTagsAndTaggings : function(tags, taggings) {
		// Process the tags and taggings,
		// For each tag will will keep object with tag label, its URI and its related tagging URI

		// First, create a map of tag URI -> tag object
		var tagsMap = {};
		$.each(tags, function(tagURI, tag){
			var label;

			// Determine label for free or controlled tag
			if (tag["http://www.w3.org/2000/01/rdf-schema#label"]) {
				label = tag["http://www.w3.org/2000/01/rdf-schema#label"][0]["value"];
			} else if (tag["http://www.holygoat.co.uk/owl/redwood/0.1/tags/name"]){
				label = tag["http://www.holygoat.co.uk/owl/redwood/0.1/tags/name"][0]["value"];
			} else {
				label = "unknown label";
			}

			//
			var schemeURI = null;
			if (tag["http://www.w3.org/2004/02/skos/core#inScheme"]) {
				schemeURI = tag["http://www.w3.org/2004/02/skos/core#inScheme"][0]["value"];
			}

			tagsMap[tagURI] = {
				label : label,
				URI   : tagURI,
				schemeURI: schemeURI
			}
		});

		// Now walk through the taggings and add the tagging URI to the tag objects
		$.each(taggings, function(taggingURI, tagging){
			var tagURI = tagging["http://www.holygoat.co.uk/owl/redwood/0.1/tags/associatedTag"][0]["value"];
			tagsMap[tagURI].taggingURI = taggingURI;
		});


		// Keep the map in the instance
		this._tagsMap = tagsMap;
		
		// Update the UI
		this._renderTags();
	},

	/**
	 * Adds a tag by label
	 *
	 */
	_addTagByLabel : function(label) {
		this._log("WS: Adding a tag by label=" + label);
		
		// Invoke the WS
		jQuery.ajax({
			url: this.options.serverBaseURL + "social/tagging/create"
					+ "?taggedItemUri=" +  encodeURIComponent(this.options.resourceURI)
					+ "&tagLabel=" + encodeURIComponent(label),
			type: "POST",
			success: $.proxy(function(data, extStatus, xhr){
				this._log("WS: Added tag by label");

				// TODO: The WS should return the tag URI and tagging URI
				// (currently only returns tagging URI in plain text)
				// So just for now - figure out random tag URI
				var tagURI = new Date().getTime();
				
				// Since this has been successful, add the tag to the model
				// and to the UI
				var tag = {
					label: label,
					URI	 : tagURI,
					taggingURI: data
				}
				this._tagsMap[tagURI] = tag;
				this._renderTag(tag);
				this._updateNoTagsMessage();

			},this),
			error: $.proxy(function(xhr, textStatus, errorThrown) {
				this._log("WS: Failed to add tag by label");

				// TODO: Remove this: Pretend this works
				// Since this has been successful, add the tag to the model
				// and to the UI
				var tagURI = new Date().getTime();
				var tag = {
					label: label,
					URI	 : tagURI,
					taggingURI: "foobar"
				}
				this._tagsMap[tagURI] = tag;
				this._renderTag(tag);
				this._updateNoTagsMessage();

			},this)
		});
	},

	_addTagByURI : function(tagURI, label, schemeURI) {
		this._log("WS: Adding a tag by URI=" + tagURI);

		// Invoke the WS
		jQuery.ajax({
			url: this.options.serverBaseURL + "social/tagging/create"
					+ "?taggedItemUri=" +  encodeURIComponent(this.options.resourceURI)
					+ "&taggingItemUri=" + encodeURIComponent(tagURI),
			type: "POST",
			success: $.proxy(function(data, extStatus, xhr){
				this._log("WS: Added tag by URI");

				// Since this has been successful, add the tag to the model
				// and to the UI
				var tag = {
					label: label,
					URI	 : tagURI,
					taggingURI: data,
					schemeURI : schemeURI
				}
				this._tagsMap[tagURI] = tag;
				this._renderTag(tag);
				this._updateNoTagsMessage();

			},this),
			error: $.proxy(function(xhr, textStatus, errorThrown) {
				this._log("WS: Failed to add tag by URI");

				// TODO: Remove this: Pretend this works
				// Since this has been successful, add the tag to the model
				// and to the UI
				var tag = {
					label: label,
					URI	 : tagURI,
					taggingURI: "foobar",
					schemeURI : schemeURI
				}
				this._tagsMap[tagURI] = tag;
				this._renderTag(tag);
				this._updateNoTagsMessage();

			},this)
		});

	},

	/**
	 * Removes tagging of a tag given a tag URI. Uses internal data model
	 * to find the tagging URI and then invokes the web service
	 */
	_removeTagging : function(tagURI){
		this._log("WS: Delete tagging");

		var taggingURI = this._tagsMap[tagURI].taggingURI;
		jQuery.ajax({
			url: taggingURI,
			type: "DELETE",
			success: $.proxy(function(data, extStatus, xhr){
				this._log("WS: Tagging deleted");

				// Since this has been successful, remove the tag
				// from the model and from the UI
				delete this._tagsMap[tagURI];
				this._unrenderTag(tagURI);

			},this),
			error: $.proxy(function(xhr, textStatus, errorThrown) {
				this._log("WS: Failed to delete tagging");

				// TODO: REMOVE THIS. Since the back-end is flaky now
				// Assume it's all good ;-)
				delete this._tagsMap[tagURI];
				this._unrenderTag(tagURI);

			},this)
		});
	},

	////////////////
	// Public API
	////////////////

	destroy: function() {
		$.Widget.prototype.destroy.apply(this, arguments); // default destroy
		// now do other stuff particular to this widget
	},

	focus: function() {
		this.hierarchyBrowserWidget.focus();
	}
});